Skip to content

Comments

feat(audit): implement platform-wide audit logging#302

Merged
betterclever merged 13 commits intomainfrom
codex/eng-174-audit-logging
Feb 21, 2026
Merged

feat(audit): implement platform-wide audit logging#302
betterclever merged 13 commits intomainfrom
codex/eng-174-audit-logging

Conversation

@betterclever
Copy link
Contributor

@betterclever betterclever commented Feb 18, 2026

Summary

Platform-wide audit logging implementation for ShipSec Studio.

Core infrastructure (feat(audit): implement platform-wide audit logging)

  • AuditLogService — fire-and-forget via queueMicrotask, never blocks requests
  • AuditLogsController — paginated list endpoint with cursor-based pagination (GET /audit-logs)
  • Database schema: audit_logs table with actor, action, resource, IP, user-agent, metadata
  • @Global() module — inject AuditLogService anywhere without module imports
  • Captures: userId, organizationId, actorType (user | api-key | internal | unknown), IP, user-agent from request context

Resource coverage

Resource Events audited
workflow create, update, delete, run, cancel
secret create, update, delete
api_key create, delete
webhook create, update, delete
artifact download
schedule create, update, delete, pause, resume, trigger
mcp_server create, update, toggle, delete
mcp_group create, update, delete, import_template
human_input resolve

Auth context threading (fix(audit): thread auth context through mcp-groups and human-inputs)

  • MCP groups controller was discarding @CurrentAuth() via unused _auth param — fixed
  • createGroup, updateGroup, deleteGroup, importTemplate now propagate real actor to audit log
  • HumanInputsService.resolve() accepts optional auth param; controller threads it through
  • resolveByToken (public token endpoint) correctly records null actor

Type system fixes

  • Extended AuditResourceType union in DB schema with 4 new types
  • Kept AuditResourceTypeSchema Zod enum in sync to avoid TS2322 in controller
  • Updated test mocks to include AuditLogService constructor arg

Test plan

  • All 646 unit tests pass, 0 failures
  • TypeScript build clean (tsc --build)
  • Audit record() calls are fire-and-forget — no request latency impact
  • Verify GET /audit-logs returns paginated entries filtered by org

🤖 Generated with Claude Code

@betterclever betterclever force-pushed the codex/eng-174-audit-logging branch from 9f87238 to 0963c3a Compare February 18, 2026 14:28
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f87238fd2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 223 to 224
setCursor(nextCursor);
load('next').catch(() => {});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use updated cursor when loading next audit log page

This handler sets cursor and immediately calls load('next'), but load reads query from the current render before the state update is applied. When users click “Load more” from the first page, the request is sent with the old cursor (undefined), so page 1 is fetched again and appended as duplicates; similar stale-state behavior also affects other flows that call setCursor(...) and then load(...) synchronously. Pass the next cursor directly into load (or trigger loading from cursor changes) so pagination requests always use the intended cursor.

Useful? React with 👍 / 👎.

import { AuditLogService } from './audit-log.service';
import {
ListAuditLogsQuerySchema,
type ListAuditLogsQueryDto,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Import audit query DTO as value for OpenAPI reflection

Using a type-only import for ListAuditLogsQueryDto strips the runtime class reference that Swagger relies on for @Query metadata, so this commit’s generated schema omits all /audit-logs query params (and the generated backend client shows query?: never for AuditLogsController_list). That makes typed SDK consumers unable to pass filters/cursor/limit even though the endpoint supports them; import the DTO as a value (or add explicit @ApiQuery declarations) to keep the contract accurate.

Useful? React with 👍 / 👎.

betterclever and others added 11 commits February 21, 2026 14:48
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
- Remove duplicate ApiKeyPermissions interface from auth/types.ts (now imported from schema)
- Add missing audit permission to controller and service spec mocks
- Add auditLogService arg to WorkflowsService constructor in run-status-cache spec

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Records create, update, delete, pause, resume, and trigger events
for workflow schedules. Extends AuditResourceType with 'schedule',
'mcp_server', 'mcp_group', and 'human_input' resource types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Records create, update, toggle, and delete events for MCP servers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Records create, update, delete, and import_template events for MCP groups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Records human_input.resolve for both authenticated and public-token
resolution paths, capturing approval status and responder identity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Previously all mcp_group audit events recorded null actor because auth
was discarded in the controller with _auth. Similarly, human_input.resolve
was not tracking the actor.

- Add auth param to createGroup, updateGroup, deleteGroup, importTemplate
- Pass auth through from controller for all mutating mcp-group endpoints
- Add auth param to human-inputs resolve() for actor tracking
- resolveByToken correctly keeps null auth (genuinely unauthenticated)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
- Add schedule, mcp_server, mcp_group, human_input to AuditResourceTypeSchema
- Fix HumanInputsService test constructor call (added AuditLogService param)
- Fix SchedulesService test constructor call (added AuditLogService param)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
@betterclever betterclever force-pushed the codex/eng-174-audit-logging branch from d1f450d to eca26eb Compare February 21, 2026 09:38
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
@betterclever betterclever merged commit 557918a into main Feb 21, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant